<?php
/**
 * | 节程 [ 节程赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 *  | Copyright (c) 2020~2029 温州惊蛰网络科技有限公司 All rights reserved.
 * +----------------------------------------------------------------------
 *  | Licensed 节程并不是自由软件，未经许可不能去掉节程相关版权
 * +----------------------------------------------------------------------
 */


namespace app\shop_admin\service;

use app\shop_admin\model\ExpressTemplate;
use app\shop_admin\model\Order;
use app\shop_admin\model\ShopConfiguration;
use think\Exception;
use think\facade\Db;

class ExpressBillService
{
    //电商ID
    private $EBusinessID;
    //电商key，
    private $AppKey;
    //请求url，正式环境地址：https://api.kdniao.com/api/EOrderService    测试环境地址：http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json
    private $ReqURL;
    private $user;
    public function __construct()
    {
        global $user;
        $this->user = $user;
        //请求url，正式环境地址：https://api.kdniao.com/api/EOrderService    测试环境地址：http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json
       // $this->ReqURL = 'http://sandboxapi.kdniao.com:8080/kdniaosandbox/gateway/exterfaceInvoke.json';
         $this->ReqURL = 'https://api.kdniao.com/api/EOrderService';
    }

    /**
     * 获取配置
     * @param int $merchant_id
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getConfig(int $merchant_id) : array{
        $find = ShopConfiguration::where('merchant_id',$merchant_id)
            ->where('type','expressBill')
            ->where('mall_id',$this->user->mall_id)
            ->find();
        return [HTTP_SUCCESS,$find];
    }

    /**
     * 修改添加配置
     * @param int $merchant_id
     * @param int $mall_id
     * @param string $config
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function setConfig(int $merchant_id, int $mall_id, string $config) : array{
        $find = ShopConfiguration::where('merchant_id',$merchant_id)
            ->where('mall_id',$mall_id)
            ->where('type','expressBill')
            ->find();
        if (empty($find))
            ShopConfiguration::create([
                'type' => 'expressBill',
                'merchant_id' => $merchant_id,
                'mall_id' => $mall_id,
                'configuration' => json_decode($config,true),
                'status' => 1
            ]);
        else
            $find->save(['configuration' => json_decode($config,true)]);
        return [HTTP_SUCCESS,'配置成功'];
    }
    /**
     * 模板列表
     * @param int $merchant_id
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function template_list(int $merchant_id){
        $template = ExpressTemplate::where('merchant_id',$merchant_id)
            ->select();
        return [HTTP_SUCCESS,$template];
    }

    /**
     * 模板详情
     * @param int $id
     * @return array
     * @throws Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function template_find(int $id){
        $template = ExpressTemplate::find($id);
        if (empty($template))
            throw new Exception('模板不存在',HTTP_INVALID);
        return [HTTP_SUCCESS,$template];
    }

    /**
     * 设置默认模板
     * @param int $id
     * @param int $merchant_id
     * @return array
     * @throws Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function template_default(int $id, int $merchant_id){
        $template = ExpressTemplate::find($id);
        if (empty($template))
            throw new Exception('模板不存在',HTTP_INVALID);
        ExpressTemplate::where('merchant_id',$merchant_id)->update(['default' => 0]);
        $template->save(['default' => 1]);
        return [HTTP_SUCCESS,$template];
    }

    /**
     * 创建模板
     * @param array $data
     * @param int $merchant_id
     * @return array
     */
    public function template_create(array $data, int $merchant_id) : array{
        $data['merchant_id'] = $merchant_id;
        $count = ExpressTemplate::where('merchant_id',$merchant_id)->count();
        if ($count == 0)
            $data['default'] = 1;
        $template = ExpressTemplate::create($data);
        if (!empty($template->id))
            return [HTTP_SUCCESS,$template];
        return [HTTP_INVALID,'添加面单模板失败'];
    }

    /**
     * 更新模板
     * @param array $data
     * @param int $id
     * @return array
     * @throws Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function template_update(array $data, int $id) : array{
        $template = ExpressTemplate::find($id);
        if (empty($template))
            throw new Exception('配置模板不存在',HTTP_INVALID);
        $is_bool = $template->save($data);
        if ($is_bool)
            return [HTTP_SUCCESS,'更新成功'];
        return [HTTP_INVALID,'更新失败'];
    }

    /**
     * 删除模板
     * @param int $id
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function template_delete(int $id) : array{
        $template = ExpressTemplate::find($id);
        $is_bool = $template->delete();
        if ($is_bool)
            return [HTTP_SUCCESS,'删除成功'];
        return [HTTP_INVALID,'删除失败'];
    }

    /**
     * 下单并且创建面单
     * @param int $order_id
     * @param string $shipper_code
     * @param string $sender
     * @return array
     * @throws Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function create(array $order_id,array $order_commodity_id, int $template_id,int $merchant_id) : array{
        $config = ShopConfiguration::where('merchant_id',$merchant_id)
            ->where('type','expressBill')
            ->value('configuration');
        $config = json_decode($config);
        if (empty($config))
            throw new Exception('未配置打印参数',HTTP_NOTACCEPT);
        $this->EBusinessID = $config->EBusinessID;
        $this->AppKey = $config->AppKey;
        $order = Order::where('id','IN',$order_id)
            ->with([
                'order_commodity' => function($sql) use ($order_commodity_id){
                    $sql->field('oc.*,c.weight,oc.group,(CASE WHEN sku_id IS NULL THEN name ELSE CONCAT(name,\'(\',(select pvs_value FROM jiecheng_sku WHERE id=sku_id),\')\') END) AS name')
                        ->alias('oc')
                        ->join('commodity c','oc.commodity_id=c.id','left')
                        ->where('oc.id','IN',$order_commodity_id)
                        ->append(['status_data']);
                }
            ])
            ->append(['status_data'])
            ->select()
            ->toArray();
        if (empty($order))
            throw new Exception('订单商品不存在',HTTP_INVALID);
        is_dir(root_path('public/expressbill')) || mkdir(root_path('public/expressbill'));
        $PrintTemplate = [];
        $template = ExpressTemplate::find($template_id);
        if (empty($template))
            throw new Exception('请选择模板', HTTP_INVALID);
        $send = ['order_id' => [],'order_commodity_id' => []];
        foreach ($order AS $key => $value) {
            $eorder = $commodity= [];
            if (empty($value['address']))throw new Exception('订单没有收货地址',HTTP_INVALID);
            if ($value['status_data'] == 2)$send['order_id'][] = $value['id'];
            $order_commodity_id = array_column($value['order_commodity'],'id');
            sort($order_commodity_id);
            $id = implode('-',$order_commodity_id);
            if (file_exists(root_path('public/expressbill') .$template_id . '-' .  $value['order_no'] . $id . '.log')){
                $PrintTemplate[] = file_get_contents(root_path('public/expressbill') .$template_id . '-' .  $value['order_no'] . $id . '.log');
                continue;
            }

            $count = 0;
            foreach ($value['order_commodity'] AS $index => $item) {
                if ($item['status_data'] == 2)$send['order_commodity_id'][] = $item['id'];
                $attach_value = Db::name('order_commodity_attach')
                    ->field('attach_value')
                    ->where('order_id',$value['id'])
                    ->where('group',$item['group'])
                    ->select()
                    ->toArray();
                if(!empty($attach_value)){
                    $attach_value = array_column($attach_value,'attach_value');
                    $item['name'] = $item['name'].implode('x',$attach_value);
                }
                $commodity[] = $this->SetCommodity($item);
                $count += $item['count'];
            }
            $sender = $this->SetSender($template);
            $receiver = $this->SetReceive($value);
            if (!empty($template->customer_name))$eorder['CustomerName'] = $template->shipper_code == $template->customer_name;
            if (!empty($template->customer_pwd))$eorder['CustomerPwd'] = $template->customer_pwd;
            if (!empty($template->send_site))$eorder['SendSite'] = $template->send_site;
            if (!empty($template->send_staff))$eorder['SendStaff'] = $template->send_staff;
            if (!empty($template->month_code))$eorder['MonthCode'] = $template->month_code;
            $eorder["ShipperCode"] = $template->shipper_code;
            $eorder["OrderCode"] = $value['order_no'];
            $eorder["PayType"] = $template->pay_type;
            $eorder["ExpType"] = $template->exp_type;
            $eorder['IsReturnPrintTemplate'] = 1;
            if ($eorder['ExpType'] == 'SF')$eorder['TemplateSize'] = 180;
            $eorder["Sender"] = $sender;
            $eorder["Receiver"] = $receiver;
            $eorder["Commodity"] = $commodity;
            $eorder['Quantity'] = $count;
            $jsonParam = json_encode($eorder, JSON_UNESCAPED_UNICODE);
            $jsonResult = $this->submitEOrder($jsonParam);
            $jsonResult = json_decode($jsonResult);
            if (empty($jsonResult))
                throw new Exception('打印错误',HTTP_NOTACCEPT);
            if (empty($jsonResult->ResultCode))
                throw new Exception('打印错误',HTTP_NOTACCEPT);
            if ($jsonResult->ResultCode <> 100)
                throw new Exception($value['order_no'].'打印出错,错误原因:'.$jsonResult->Reason,HTTP_INVALID);
            $generator = new \Picqer\Barcode\BarcodeGeneratorPNG();
            $generator = $generator->getBarcode($jsonResult->Order->LogisticCode,$generator::TYPE_CODE_128);
            $generator = 'data:image/png;base64,'.base64_encode($generator);
            $html_template = file_get_contents(root_path('app').'expressbill.html');
            $goodsList = array_column($commodity,'GoodsName');
            $goodsName = '';
            foreach ($goodsList AS $index => $item)
                $goodsName .= '<li>'.$item.'</li>';
            $search = [
                '{bar code}',
                '{bar code image}',
                '{receiving name}',
                '{receiving mobile}',
                '{receiving address}',
                '{sender name}',
                '{sender mobile}',
                '{sender address}',
                '{goods list}',
                '{MarkDestination}',
                '{PackageName}',
                '{OriginName}',
                '{time}'
            ];
            $replace = [
                $jsonResult->Order->LogisticCode,
                $generator,
                $receiver['Name'],
                $receiver['Mobile'],
                $receiver['ProvinceName'].$receiver['CityName'].$receiver['ExpAreaName'].$receiver['Address'],
                $sender['Name'],
                $sender['Mobile'],
                $sender['ProvinceName'].$sender['CityName'].$sender['ExpAreaName'].$sender['Address'],
                $goodsName,
                $jsonResult->Order->MarkDestination	?? $this->MarkDestination($jsonResult),
                $jsonResult->Order->PackageName ?? $this->PackageName($jsonResult),
                $jsonResult->Order->OriginName ?? $this->OriginName($jsonResult),
                date('Y/m/d H:i:s')
            ];
            $html_template = str_replace($search,$replace,$html_template);
            file_put_contents(root_path('public/expressbill') .$template_id . '-' .  $value['order_no'] . $id . '.log', $html_template);
            $PrintTemplate[] = $html_template;
        }
        if (!empty($send['order_id'])) {
            $data['is_print'] = 1;
            if ($template->is_notice == 1)$data['status'] = 3;
            Db::name('order')
                ->where('id', 'IN', $send['order_id'])
                ->update($data);
        }
        $data = [];
        if (!empty($send['order_commodity_id'])) {
            $data['is_print'] = 1;
            $data['logistics_no'] = $jsonResult->Order->LogisticCode;
            $data['deliver_time'] = date('Y-m-d H:i:s');
            $data['express_company'] = Db::name('express')
                ->where('code',$template['shipper_code'])
                ->value('name');
            if ($template->is_notice == 1)$data['status'] = 3;
            Db::name('order_commodity')
                ->where('id', 'IN', $send['order_commodity_id'])
                ->update($data);
        }
        return [HTTP_SUCCESS, $PrintTemplate];
    }
    private function MarkDestination($jsonResult) : string{
        if ($jsonResult->Order->ShipperCode == 'SF'){
            return json_decode($jsonResult->Order->ShipperInfo)->Details[0]->Detail->DestRouteLabel;
        }
        return ' ';
    }
    private function PackageName($jsonResult) : string{
        if (!empty($jsonResult->Order->PackageCode))
            return $jsonResult->Order->PackageCode;
//        if ($jsonResult->Order->ShipperCode == 'SF')
//            return json_decode($jsonResult->Order->ShipperInfo)->Details[0]->Detail->CodingMapping;
        return ' ';
    }
    private function OriginName($jsonResult) : string{
        if (!empty($jsonResult->Order->OriginCode))
            return $jsonResult->Order->OriginCode;

        return ' ';
    }
    /**
     * 配置发货人参数
     * @param string $sender
     * @return array
     */
    private function SetSender(ExpressTemplate $sender) : array{
        return [
            'Name' => $sender->name,
            'ProvinceName' => $sender->province_name,
            'Mobile' => $sender->mobile,
            'Tel' => $sender->tel,
            'CityName' => $sender->city_name,
            'ExpAreaName' => $sender->exp_area_name,
            'Address' => $sender->address
        ];
    }

    /**
     * 配置收货人参数
     * @param Order $order
     * @return array
     */
    private function SetReceive(array $value) : array{
        $address = explode(',',$value['address']);
        return [
            'Name' => $value['consignee'],
            'Mobile' => $value['iphone'],
            'ProvinceName' => $address[0],
            'CityName' => $address[1],
            'ExpAreaName' => $address[2],
            'Address' => $address[3]
        ];
    }

    /**
     * 配置货物参数
     * @param Order $order
     * @return array
     */
    private function SetCommodity($commodity){
        $array = [
            'GoodsName' => $commodity['name'],
            'GoodsQuantity' => $commodity['count'],
            'GoodsWeight' => $commodity['weight']
        ];
        return $array;
    }

    //------------------------------------------------------------------------------------------------------------------
    /**
     * Json方式 调用电子面单接口
     */
    private function submitEOrder($requestData){
        $datas = array(
            'EBusinessID' => $this->EBusinessID,
            'RequestType' => '1007',
            'RequestData' => urlencode($requestData) ,
            'DataType' => '2',
        );
        $datas['DataSign'] = $this->encrypt($requestData, $this->AppKey);
        $result = $this->sendPost($this->ReqURL, $datas);

        //根据公司业务处理返回的信息......

        return $result;
    }


    /**
     *  post提交数据
     * @param  string $url 请求Url
     * @param  array $datas 提交的数据
     * @return url响应返回的html
     */
    function sendPost($url, $datas) {
        $temps = array();
        foreach ($datas as $key => $value) {
            $temps[] = sprintf('%s=%s', $key, $value);
        }
        $post_data = implode('&', $temps);
        $url_info = parse_url($url);
        if(empty($url_info['port']))
        {
            $url_info['port']=80;
        }
        $httpheader = "POST " . $url_info['path'] . " HTTP/1.0\r\n";
        $httpheader.= "Host:" . $url_info['host'] . "\r\n";
        $httpheader.= "Content-Type:application/x-www-form-urlencoded\r\n";
        $httpheader.= "Content-Length:" . strlen($post_data) . "\r\n";
        $httpheader.= "Connection:close\r\n\r\n";
        $httpheader.= $post_data;
        $fd = fsockopen($url_info['host'], $url_info['port']);
        fwrite($fd, $httpheader);
        $gets = "";
        $headerFlag = true;
        while (!feof($fd)) {
            if (($header = @fgets($fd)) && ($header == "\r\n" || $header == "\n")) {
                break;
            }
        }
        while (!feof($fd)) {
            $gets.= fread($fd, 128);
        }
        fclose($fd);

        return $gets;
    }

    /**
     * 电商Sign签名生成
     * @param data 内容
     * @param appkey Appkey
     * @return DataSign签名
     */
    private function encrypt($data, $appkey) {
        return urlencode(base64_encode(md5($data.$appkey)));
    }
    /**************************************************************
     *
     *  使用特定function对数组中所有元素做处理
     *  @param  string  &$array     要处理的字符串
     *  @param  string  $function   要执行的函数
     *  @return boolean $apply_to_keys_also     是否也应用到key上
     *  @access public
     *
     *************************************************************/
    private function arrayRecursive(&$array, $function, $apply_to_keys_also = false)
    {
        static $recursive_counter = 0;
        if (++$recursive_counter > 1000) {
            die('possible deep recursion attack');
        }
        foreach ($array as $key => $value) {
            if (is_array($value)) {
                $this->arrayRecursive($array[$key], $function, $apply_to_keys_also);
            } else {
                $array[$key] = $function($value);
            }

            if ($apply_to_keys_also && is_string($key)) {
                $new_key = $function($key);
                if ($new_key != $key) {
                    $array[$new_key] = $array[$key];
                    unset($array[$key]);
                }
            }
        }
        $recursive_counter--;
    }


    /**************************************************************
     *
     *  将数组转换为JSON字符串（兼容中文）
     *  @param  array   $array      要转换的数组
     *  @return string      转换得到的json字符串
     *  @access public
     *
     *************************************************************/
    private function JSON($array) {
        $this->arrayRecursive($array, 'urlencode', true);
        $json = json_encode($array);
        return urldecode($json);
    }
}